package main.java.FSMDE.views;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import main.java.ERMDE.models.Attribute;
import main.java.ERMDE.models.Diagram;
import main.java.ERMDE.models.Entity;
import main.java.FSMDE.controllers.ComponentController;
import main.java.FSMDE.controllers.IComponentController;
import main.java.FSMDE.infrastructure.visual.ComponentTreeNode;
import main.java.FSMDE.models.ComplexComponent;
import main.java.FSMDE.models.Component;
import main.java.FSMDE.models.ComponentCollection;
import main.java.FSMDE.models.ComponentType;
import main.java.FSMDE.models.RelatedComponent;

import com.jgoodies.forms.factories.CC;
import com.jgoodies.forms.layout.FormLayout;


public class ComponentView implements IComponentView {
	private IComponentController controller;

	private JTree components;
	private JTextField name;
	private JScrollPane scrollPane1;
	private JButton createComponentButton;
	private JLabel genericTypeText;
	private JTextField genericTypeName;
	@SuppressWarnings("rawtypes")
	private JComboBox type;
	private JPanel panel1;
	private JPanel newComponentPanel;
	private JPanel relatedEntitiesPanel;
	private DefaultTreeModel componentModel;
	private ComponentCollection componentModelList;
	private JLabel labelSetComponentTo;
	@SuppressWarnings("rawtypes")
	private JComboBox comboSetComponentTo;
	private Map<Component, ComponentTreeNode> componentTreeNodes;

	private JComboBox<Component> reComboComps;
	@SuppressWarnings("rawtypes")
	private JComboBox reComboDiagrams;
	@SuppressWarnings("rawtypes")
	private JComboBox reComboEntities;
	@SuppressWarnings("rawtypes")
	private JComboBox reComboAttr;

	private JButton createRelationButton;

	private ArrayList<Diagram> ermDiagrams;

	private HashMap<Diagram, JComboBox<Entity>> reComboEntitiesMap;

	private HashMap<Entity, JComboBox<Attribute>> reComboAttributesMap;

	public ComponentView() {
		generatePanel();
	}

	public void generatePanel() {
		this.componentTreeNodes = new HashMap<Component, ComponentTreeNode>();
		if (ermDiagrams == null)
			this.ermDiagrams = new ArrayList<main.java.ERMDE.models.Diagram>();
		initComponents();
		componentModel = new DefaultTreeModel(new DefaultMutableTreeNode(
				"Components"));
		components.setModel(componentModel);
		components.addTreeSelectionListener(new TreeSelectionListener() {
			@Override
			public void valueChanged(TreeSelectionEvent e) {
				if (components.getLastSelectedPathComponent() instanceof ComponentTreeNode) {
					((ComponentTreeNode) components
							.getLastSelectedPathComponent()).getComponent();
				}
			}
		});

		components.addKeyListener(new KeyListener() {
			@Override
			public void keyTyped(KeyEvent e) {
			}

			@Override
			public void keyPressed(KeyEvent e) {
			}

			@Override
			public void keyReleased(KeyEvent e) {
			}
		});
		createComponentButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent actionEvent) {
				if (name.getText().length() != 0) {
					createComponent();
				}
			}
		});
		type.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent actionEvent) {

				if (type.getSelectedItem() == ComponentType.Generic) {
					genericTypeName.setVisible(true);
					genericTypeText.setVisible(true);
				} else {
					genericTypeName.setVisible(false);
					genericTypeText.setVisible(false);
				}
			}
		});
		this.components.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				TreePath path = components.getSelectionPath();
				if (e.getKeyCode() == KeyEvent.VK_DELETE) {

					DefaultMutableTreeNode nodeItem = (DefaultMutableTreeNode) path
							.getLastPathComponent();
					Component deletedComponent = componentModelList
							.get(nodeItem.toString().substring(0,
									nodeItem.toString().indexOf(" : ")));

					controller.deleteElement(path);
					DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
							.getLastPathComponent();
					componentModel.removeNodeFromParent(node);
					componentModelList = controller.getComponents();

					comboSetComponentTo.removeItem(deletedComponent);
				}
			}
		});

		this.type.addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent e) {
			}
		});
		cleanView();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void initComponents() {
		panel1 = new JPanel();
		newComponentPanel = new JPanel();
		relatedEntitiesPanel = new JPanel();
		name = new JTextField();
		scrollPane1 = new JScrollPane();
		components = new JTree();
		genericTypeText = new JLabel();
		genericTypeName = new JTextField();
		Vector<ComponentType> vType = new Vector<ComponentType>();
		for (ComponentType ct : ComponentType.values()) {
			vType.add(ct);
		}
		java.util.Collections.sort(vType, new Comparator<ComponentType>() {

			@Override
			public int compare(ComponentType o1, ComponentType o2) {
				return o1.toString().compareTo(o2.toString());
			}
		});
		type = new JComboBox(vType);
		createComponentButton = new JButton();

		labelSetComponentTo = new JLabel("Set component to");
		Vector itemsComboSetComponentTo = new Vector();
		itemsComboSetComponentTo.add("Interface");
		comboSetComponentTo = new JComboBox(itemsComboSetComponentTo);

		reComboComps = new JComboBox<Component>();

		Vector<main.java.ERMDE.models.Diagram> comboDiagramsElements = new Vector<Diagram>();
		comboDiagramsElements.addAll(ermDiagrams);
		reComboDiagrams = new JComboBox(comboDiagramsElements);

		reComboEntitiesMap = new HashMap<main.java.ERMDE.models.Diagram, JComboBox<Entity>>();
		reComboAttributesMap = new HashMap<Entity, JComboBox<Attribute>>();
		for (main.java.ERMDE.models.Diagram diagram : comboDiagramsElements) {
			Vector<Entity> comboEntityElements = new Vector<Entity>();
			for (Entity entity : diagram.getEntities()) {
				comboEntityElements.add(entity);

				Vector<Attribute> comboAttributeElements = new Vector<Attribute>();
				for (Attribute attribute : entity.getAttributes()) {
					comboAttributeElements.add(attribute);
				}
				reComboAttributesMap.put(entity, new JComboBox<Attribute>(
						comboAttributeElements));
			}
			reComboEntitiesMap.put(diagram, new JComboBox<Entity>(
					comboEntityElements));
		}
		createRelationButton = new JButton();

		createRelationButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (reComboComps.getSelectedItem() == null) {
					JOptionPane
							.showMessageDialog(
									null,
									"There is no Component selected, please select one on the drop-down list.",
									"Invalid Related Component",
									JOptionPane.QUESTION_MESSAGE);
				} else if (reComboAttr.isVisible()) {
					// reComboAttr visible means a relationship has been
					// selected
					// only if a component has been selected

					RelatedComponent component = (RelatedComponent) reComboComps
							.getSelectedItem();
					main.java.ERMDE.models.Diagram diagram = (main.java.ERMDE.models.Diagram) reComboDiagrams
							.getSelectedItem();
					Entity entity = (Entity) reComboEntities.getSelectedItem();
					Attribute attribute = (Attribute) reComboAttr
							.getSelectedItem();

					if (component != null) {
						try {
							Component relatedTextField = component
									.setRelationToErmAttribute(diagram, entity,
											attribute);
							ComponentTreeNode componentTreeNode = new ComponentTreeNode(
									relatedTextField);
							ComponentTreeNode treeNodeFather = componentTreeNodes
									.get(component);
							treeNodeFather.add(componentTreeNode);
							componentTreeNodes.put(relatedTextField,
									componentTreeNode);
							cleanView();
						} catch (Exception e1) {
							e1.printStackTrace();
						}
					}
				}
			}
		});

		final JLabel entityRealtionLabel = createLabel("Entity");
		final JLabel attributeRelationLabel = createLabel("Attribute");

		entityRealtionLabel.setVisible(false);
		attributeRelationLabel.setVisible(false);

		// ======== action listeners ===========================
		reComboDiagrams.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {

				if (reComboEntities != null) {
					reComboEntities.setVisible(false);
					entityRealtionLabel.setVisible(false);
				}
				reComboEntities = reComboEntitiesMap.get(reComboDiagrams
						.getSelectedItem());
				if (reComboEntities != null) {
					reComboEntities.setVisible(true);
					entityRealtionLabel.setVisible(true);
				}
				if (reComboAttr != null) {
					reComboAttr.setVisible(false);
					attributeRelationLabel.setVisible(false);
				}
			}
		});

		for (main.java.ERMDE.models.Diagram diagram : ermDiagrams) {
			reComboEntitiesMap.get(diagram).addActionListener(
					new ActionListener() {

						@Override
						public void actionPerformed(ActionEvent e) {

							if (reComboAttr != null) {
								reComboAttr.setVisible(false);
								attributeRelationLabel.setVisible(false);
							}
							reComboAttr = reComboAttributesMap
									.get(reComboEntities.getSelectedItem());
							if (reComboAttr != null) {
								reComboAttr.setVisible(true);
								attributeRelationLabel.setVisible(true);
							}
						}
					});
		}

		// ======== panel1 ========
		{
			panel1.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
				public void propertyChange(java.beans.PropertyChangeEvent e) {
					if ("border".equals(e.getPropertyName()))
						throw new RuntimeException();
				}
			});

			// ======== scrollPane1 ========
			{
				// components.setPreferredSize(new Dimension(400, 100));
				scrollPane1.setViewportView(components);
				// scrollPane1.setPreferredSize(new Dimension(600, 100));
			}

			panel1.setLayout(new FormLayout("50px,600px",
					"30px,100px,30px,120px, 30px, 120px"));
			// Components
			panel1.add(createLabel("Components"), CC.xywh(1, 1, 2, 1));
			panel1.add(scrollPane1, CC.xywh(1, 2, 2, 1));
			panel1.add(createLabel("Create Component"), CC.xywh(1, 3, 2, 1));
			panel1.add(newComponentPanel, CC.xy(2, 4));
			panel1.add(createLabel("Related Entities"), CC.xywh(1, 5, 2, 1));
			panel1.add(relatedEntitiesPanel, CC.xy(2, 6));

			newComponentPanel.setLayout(new FormLayout(
					"115px,200px, 6dlu, 150px", "30px,30px,30px,30px"));

			// Name
			newComponentPanel.add(createLabel("Name"), CC.xy(1, 1));
			newComponentPanel.add(name, CC.xy(2, 1));
			// Type
			newComponentPanel.add(createLabel("Type"), CC.xy(1, 2));
			newComponentPanel.add(type, CC.xy(2, 2));
			// TypeName
			newComponentPanel.add(createLabel("TypeName"), CC.xy(1, 3));
			newComponentPanel.add(genericTypeName, CC.xy(2, 3));

			// Set Component to Label
			newComponentPanel.add(labelSetComponentTo, CC.xy(1, 4));
			newComponentPanel.add(comboSetComponentTo, CC.xy(2, 4));

			// ---- createComponentButton ----
			createComponentButton.setText("Create Component");
			newComponentPanel.add(createComponentButton, CC.xy(4, 4));

			relatedEntitiesPanel.setLayout(new FormLayout(
					"115px,200px, 6dlu, 150px", "30px,30px,30px,30px"));

			relatedEntitiesPanel.add(createLabel("Component"), CC.xy(1, 1));
			relatedEntitiesPanel.add(reComboComps, CC.xy(2, 1));

			relatedEntitiesPanel.add(createLabel("Diagram"), CC.xy(1, 2));
			relatedEntitiesPanel.add(reComboDiagrams, CC.xy(2, 2));

			relatedEntitiesPanel.add(entityRealtionLabel, CC.xy(1, 3));
			relatedEntitiesPanel.add(attributeRelationLabel, CC.xy(1, 4));

			createRelationButton.setText("Create Relation");
			relatedEntitiesPanel.add(createRelationButton, CC.xy(4, 1));

			for (Diagram diagram : ermDiagrams) {
				// System.out.println(diagram.getId() + " " +
				// diagram.getName());
				relatedEntitiesPanel.add(reComboEntitiesMap.get(diagram),
						CC.xy(2, 3));
				reComboEntitiesMap.get(diagram).setVisible(false);
				for (Entity entity : diagram.getEntities()) {
					// System.out.println(entity.getName());
					relatedEntitiesPanel.add(reComboAttributesMap.get(entity),
							CC.xy(2, 4));
					reComboAttributesMap.get(entity).setVisible(false);
				}
			}

		}
	}

	private JLabel createLabel(String text) {
		JLabel myLabel = new JLabel(text);
		return myLabel;
	}

	@Override
	public void setController(ComponentController componentController) {
		this.controller = componentController;
	}

	@Override
	public void setComponents(ComponentCollection components) {
		this.componentModelList = components;
		this.controller.setComponents(components);
		generateComponentTree(this.componentModelList,
				(DefaultMutableTreeNode) componentModel.getRoot());
		populateComboBoxs(components);
	}

	@SuppressWarnings("unchecked")
	private void populateComboBoxs(ComponentCollection components) {
		for (Component component : components) {
			if (component instanceof ComplexComponent) {
				comboSetComponentTo.addItem(component);
				populateComboBoxs(((ComplexComponent) component)
						.getComponents());
			}
			if (component instanceof RelatedComponent) {
				reComboComps.addItem(component);
			}
		}
	}

	private void generateComponentTree(Iterable<Component> components,
			DefaultMutableTreeNode node) {
		for (Component comp : components) {
			DefaultMutableTreeNode newNode = new ComponentTreeNode(comp);
			node.add(newNode);
			componentTreeNodes.put(comp, (ComponentTreeNode) newNode);
			if (comp instanceof ComplexComponent) {
				generateComponentTree(
						((ComplexComponent) comp).getComponents(), newNode);
			}
		}
	}

	@Override
	public String getName() {
		return name.getText();
	}

	@Override
	public ComponentType getComponentType() {
		return (ComponentType) type.getItemAt(type.getSelectedIndex());
	}

	public String getGenericTypeName() {
		return genericTypeName.getText();
	}

	@SuppressWarnings("unchecked")
	private void createComponent() {

		ComplexComponent componentFather = null;
		Object auxiliar = comboSetComponentTo.getSelectedItem();
		if (auxiliar instanceof ComplexComponent) {
			componentFather = (ComplexComponent) auxiliar;
		}
		Component componentCreated = this.controller
				.addNewComponent(componentFather);
		if (componentCreated != null) {
			ComponentTreeNode componentTreeNode = new ComponentTreeNode(
					componentCreated);
			if (componentFather == null) {
				((DefaultMutableTreeNode) componentModel.getRoot())
						.add(componentTreeNode);
			} else {
				ComponentTreeNode treeNodeFather = this.componentTreeNodes
						.get(componentFather);
				treeNodeFather.add(componentTreeNode);
			}
			componentTreeNodes.put(componentCreated, componentTreeNode);
			cleanView();
		}
		if (componentCreated instanceof ComplexComponent) {
			comboSetComponentTo.addItem(componentCreated);
		}
		if (componentCreated instanceof RelatedComponent) {
			reComboComps.addItem(componentCreated);
		}
	}

	private void cleanView() {
		this.name.setText("");
		this.genericTypeName.setText("");
		this.type.setSelectedIndex(0);
		components.revalidate();
		components.repaint();
		components.updateUI();
		components.clearSelection();
	}

	@Override
	public Object getInternalFrame() {
		return panel1;
	}

	@Override
	public void setErmDiagrams(ArrayList<Diagram> ermDiagrams) {
		this.ermDiagrams = ermDiagrams;
		this.generatePanel();
	}

}
